package com.iotechn.unimall.biz.service.distribution;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.iotechn.unimall.biz.service.config.ConfigBizService;
import com.iotechn.unimall.core.Const;
import com.iotechn.unimall.core.exception.AppServiceException;
import com.iotechn.unimall.core.exception.ExceptionDefinition;
import com.iotechn.unimall.core.exception.ServiceException;
import com.iotechn.unimall.core.util.DateUtils;
import com.iotechn.unimall.core.util.MoneyUtils;
import com.iotechn.unimall.data.component.CacheComponent;
import com.iotechn.unimall.data.domain.*;
import com.iotechn.unimall.data.dto.DistributionOrderDTO;
import com.iotechn.unimall.data.dto.DistributionRankingDTO;
import com.iotechn.unimall.data.dto.DistributionUserDTO;
import com.iotechn.unimall.data.dto.distribution.DistributionDetailExcel;
import com.iotechn.unimall.data.dto.distribution.DistributionStatisicDTO;
import com.iotechn.unimall.data.dto.distribution.DistributionStatisicExcelExport;
import com.iotechn.unimall.data.dto.user.UserAccountDTO;
import com.iotechn.unimall.data.dto.userCard.UserCardDetailedExcel;
import com.iotechn.unimall.data.dto.userCard.UserStoreConsume;
import com.iotechn.unimall.data.enums.DistributionBalanceDetailType;
import com.iotechn.unimall.data.enums.DistributionDeductionStatusType;
import com.iotechn.unimall.data.enums.DistributionStrategyMergeType;
import com.iotechn.unimall.data.enums.DistributionStrategyType;
import com.iotechn.unimall.data.mapper.*;
import com.iotechn.unimall.data.model.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: rize
 * Date: 2019/11/28
 * Time: 14:20
 */
@Service
public class DistributionBizService {

    @Autowired
    private DistributionMapper distributionMapper;

    @Autowired
    private DistributionBalanceDetailMapper distributionBalanceDetailMapper;

    @Autowired
    private DistributionStrategyMapper distributionStrategyMapper;

    @Autowired
    private DistributionDeductionMapper distributionDeductionMapper;

    @Autowired
    private SpuMapper spuMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CacheComponent cacheComponent;

    @Autowired
    private ConfigBizService configBizService;

    @Autowired
    private UserCardMapper userCardMapper;

    public static final String CA_DISTRIBUTION_STRATEGY_SPU = "CA_DISTRIBUTION_STRATEGY_SPU_";

    public void distribute(OrderDO order, OrderSkuDO item) {
        DistributionStrategyDO strategy = this.getSpuStrategy(item.getSpuId());
        if (strategy != null) {
            //获取到策略
            Long distributionId = order.getDistributionId();
            DistributionDO distributionDO = distributionMapper.selectById(distributionId);
            int radio = strategy.getRadio();
            UserDO userDO = userMapper.selectById(distributionDO.getUserId());
            Integer groupMaster = userDO.getGroupMaster();
            if (groupMaster == 1) {
                Integer groupMasterRadio = configBizService.getMerchantConfig().getGroupMasterRadio() == null ? 0 : configBizService.getMerchantConfig().getGroupMasterRadio();
                radio = radio * (100 + groupMasterRadio) / 100;
            }
            int skuTotalPrice = (int)(item.getPrice() * item.getNum());
            if (distributionDO.getLevel() == 3) {
                long secondId = this.incFreezeBalance(skuTotalPrice * radio * strategy.getThirdRadio() / 1000000,
                        distributionDO.getId(), order.getOrderNo(), item.getSpuTitle() + "-" + item.getTitle() + "-冻结佣金");
                if (secondId > 0) {
                    long firstId = this.incFreezeBalance(skuTotalPrice * radio * strategy.getSecondRadio() / 1000000,
                            secondId, order.getOrderNo(), item.getSpuTitle() + "-" + item.getTitle() + "-冻结佣金");
                    if (firstId > 0) {
                        this.incFreezeBalance(skuTotalPrice * radio * strategy.getFirstRadio() / 1000000,
                                firstId, order.getOrderNo(), item.getSpuTitle() + "-" + item.getTitle() + "-冻结佣金");
                    }
                }
            } else if (distributionDO.getLevel() == 2) {
                // 首先判断 上级销售策略
                int realSecondRadio;
                if (strategy.getMergeType() == DistributionStrategyMergeType.SUPERPOSITION.getCode()) {
                    realSecondRadio = strategy.getSecondRadio() + strategy.getThirdRadio();
                } else {
                    realSecondRadio = strategy.getSecondRadio() > strategy.getThirdRadio() ? strategy.getSecondRadio() : strategy.getThirdRadio();
                }
                long firstId = this.incFreezeBalance(skuTotalPrice * radio * realSecondRadio / 1000000,
                        distributionDO.getId(), order.getOrderNo(), item.getSpuTitle() + "-" + item.getTitle() + "-冻结佣金");
                if (firstId > 0) {
                    this.incFreezeBalance(skuTotalPrice * radio * strategy.getFirstRadio() / 1000000,
                            firstId, order.getOrderNo(), item.getSpuTitle() + "-" + item.getTitle() + "-冻结佣金");
                }
            } else if (distributionDO.getLevel() == 1) {
                int realFirstRadio;
                if (strategy.getMergeType() == DistributionStrategyMergeType.SUPERPOSITION.getCode()) {
                    realFirstRadio = strategy.getFirstRadio() + strategy.getSecondRadio() + strategy.getThirdRadio();
                } else {
                    realFirstRadio = strategy.getFirstRadio() > strategy.getSecondRadio() ? strategy.getFirstRadio() : (strategy.getSecondRadio() > strategy.getThirdRadio() ? strategy.getSecondRadio() : strategy.getThirdRadio());
                }
                this.incFreezeBalance(skuTotalPrice * radio * realFirstRadio / 1000000,
                        distributionDO.getId(), order.getOrderNo(), item.getSpuTitle() + "-" + item.getTitle() + "-冻结佣金");
            }
        }
    }

    /**
     * 获取商品策略
     *
     * @param spuId
     * @return
     */
    private DistributionStrategyDO getSpuStrategy(Long spuId) {
        DistributionStrategyDO distributionStrategyDO = cacheComponent.getObj(CA_DISTRIBUTION_STRATEGY_SPU + spuId, DistributionStrategyDO.class);
        if (distributionStrategyDO == null) {
            //step1.1. 查询SPU策略
            List<DistributionStrategyDO> strategySpuList = distributionStrategyMapper
                    .selectList(new EntityWrapper<DistributionStrategyDO>()
                            .eq("type_id", spuId)
                            .eq("type", DistributionStrategyType.GOODS.getCode()));
            if (CollectionUtils.isEmpty(strategySpuList)) {
                //step2.1. 查询Category策略
                SpuDO spuDO = spuMapper.selectById(spuId);
                Long categoryId = spuDO.getCategoryId();
                List<DistributionStrategyDO> strategyCategoryList = distributionStrategyMapper
                        .selectList(new EntityWrapper<DistributionStrategyDO>()
                                .eq("type_id", categoryId)
                                .eq("type", DistributionStrategyType.CATEGORY.getCode()));
                if (CollectionUtils.isEmpty(strategyCategoryList)) {
                    //step3.1. 查询全部策略
                    List<DistributionStrategyDO> strategyAllList = distributionStrategyMapper
                            .selectList(new EntityWrapper<DistributionStrategyDO>()
                                    .eq("type_id", 0)
                                    .eq("type", DistributionStrategyType.ALL.getCode()));
                    if (CollectionUtils.isEmpty(strategyAllList)) {
                        DistributionStrategyDO noneDO = new DistributionStrategyDO();
                        noneDO.setType(DistributionStrategyType.CACHE_NONE.getCode());
                        cacheComponent.putObj(CA_DISTRIBUTION_STRATEGY_SPU + spuId, noneDO, Const.CACHE_ONE_DAY);
                        return null;
                    } else {
                        //step3.2. 使用全部策略
                        cacheComponent.putObj(CA_DISTRIBUTION_STRATEGY_SPU + spuId, strategyAllList.get(0), Const.CACHE_ONE_DAY);
                        return strategyAllList.get(0);
                    }
                } else {
                    //step2.2 使用Category策略
                    cacheComponent.putObj(CA_DISTRIBUTION_STRATEGY_SPU + spuId, strategyCategoryList.get(0), Const.CACHE_ONE_DAY);
                    return strategyCategoryList.get(0);
                }
            } else {
                //1.2. 使用SPU策略
                cacheComponent.putObj(CA_DISTRIBUTION_STRATEGY_SPU + spuId, strategySpuList.get(0), Const.CACHE_ONE_DAY);
                return strategySpuList.get(0);
            }
        } else {
            if (distributionStrategyDO.getType() == DistributionStrategyType.CACHE_NONE.getCode()) {
                return null;
            } else {
                return distributionStrategyDO;
            }
        }
    }


    /**
     * 增加分销者 冻结余额
     *
     * @param delta
     * @param distributionId
     * @param orderNo
     * @param description
     * @return 返回 parentId
     */
    public long incFreezeBalance(Integer delta, Long distributionId, String orderNo, String description) {
        //将 Distribution 表 和 明细表查出，并加上行锁。此处必须同时拿到余额表的行锁 与 余额明细表的行锁。
        Date now = new Date();
        DistributionDO distributionDO = distributionMapper.selectByIdForUpdate(distributionId);
        if (distributionDO != null) {
            DistributionBalanceDetailDO lastDetailDO = distributionBalanceDetailMapper.getDistributionUserLastForUpdate(distributionId);
            if (lastDetailDO == null) {
                // 未产生明细，则余额为0
                lastDetailDO = new DistributionBalanceDetailDO();
                lastDetailDO.setBalance(0);
                lastDetailDO.setFreezeBalance(0);
            }
            //校验明细表与余额表是否数据一致
            if (!lastDetailDO.getFreezeBalance().equals(distributionDO.getFreezeBalance()) || !lastDetailDO.getBalance().equals(distributionDO.getBalance())) {
                // 出现数据不一致的情况。  不再对此用户进行分销提成。 未有人工干预，理论上绝对一致
            } else {
                //更新操作
                if (delta != 0) {
                    //更新主表
                    distributionMapper.incFreezeBalance(distributionId, delta);
                    //更新明细表
                    DistributionBalanceDetailDO balanceDetailDO = new DistributionBalanceDetailDO();
                    balanceDetailDO.setFreezeBalance(distributionDO.getFreezeBalance() + delta);
                    balanceDetailDO.setFreezeBalanceDelta(delta);
                    balanceDetailDO.setBalance(distributionDO.getBalance());
                    balanceDetailDO.setRealBalance(distributionDO.getRealBalance());
                    balanceDetailDO.setTotalBalance(distributionDO.getTotalBalance());
                    balanceDetailDO.setTotalRealBalance(distributionDO.getTotalRealBalance());
                    balanceDetailDO.setRealBalanceDelta(0);
                    balanceDetailDO.setBalanceDelta(0);
                    balanceDetailDO.setOrderNo(orderNo);
                    balanceDetailDO.setDescription(description);
                    balanceDetailDO.setGmtUpdate(now);
                    balanceDetailDO.setGmtCreate(now);
                    balanceDetailDO.setType(DistributionBalanceDetailType.Expense_deduction.getCode());
                    balanceDetailDO.setUserId(distributionDO.getUserId());
                    balanceDetailDO.setDistributionId(distributionId);
                    distributionBalanceDetailMapper.insert(balanceDetailDO);
                }
            }
            return distributionDO.getParentId();
        }
        return 0;
    }


    /**
     * 解冻分销用户佣金
     *
     * @param delta
     * @param distributionId
     * @param orderNo
     * @param description
     */
    public void unfreezeBalance(Integer delta, Long distributionId, String orderNo, String description) throws ServiceException {
        Date now = new Date();
        DistributionDO distributionDO = distributionMapper.selectByIdForUpdate(distributionId);
        if (distributionDO != null) {
            if (distributionDO.getFreezeBalance() < delta) {
                throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_FREEZE_BALANCE_NOT_ENOUGH);
            }
            //更新操作
            //DistributionBalanceDetailDO lastDetailDO = distributionBalanceDetailMapper.getDistributionUserLastForUpdate(distributionId);
            //if (!lastDetailDO.getFreezeBalance().equals(distributionDO.getFreezeBalance())) {
            // 出现数据不一致的情况。  不再对此用户进行分销提成。 未有人工干预，理论上绝对一致
            //}
            //else {
            if (delta != 0) {
                //更新主表
                distributionMapper.freezeBalance(distributionId, -delta);
                //更新明细表
                DistributionBalanceDetailDO balanceDetailDO = new DistributionBalanceDetailDO();
                balanceDetailDO.setFreezeBalance(distributionDO.getFreezeBalance() - delta);
                balanceDetailDO.setFreezeBalanceDelta(-delta);
                balanceDetailDO.setBalance(distributionDO.getBalance() + delta);
                balanceDetailDO.setRealBalance(distributionDO.getRealBalance() + delta);
                balanceDetailDO.setTotalBalance(distributionDO.getTotalBalance() + delta);
                balanceDetailDO.setTotalRealBalance(distributionDO.getTotalRealBalance() + delta);
                balanceDetailDO.setRealBalanceDelta(delta);
                balanceDetailDO.setBalanceDelta(delta);
                balanceDetailDO.setOrderNo(orderNo);
                balanceDetailDO.setDescription(description);
                balanceDetailDO.setGmtUpdate(now);
                balanceDetailDO.setGmtCreate(now);
                balanceDetailDO.setType(DistributionBalanceDetailType.Expense_deduction.getCode());
                balanceDetailDO.setUserId(distributionDO.getUserId());
                balanceDetailDO.setDistributionId(distributionDO.getId());
                distributionBalanceDetailMapper.insert(balanceDetailDO);
            }
            //}
        }
    }
    /**
     * @param orderNo
     * @throws ServiceException
     */
    //解冻订单
    public void unfreezeOrder(String orderNo) throws ServiceException {
        Wrapper<DistributionBalanceDetailDO> wrapper = new EntityWrapper<DistributionBalanceDetailDO>();
        if (orderNo != null) {
            wrapper.eq("order_no", orderNo);
        }
        wrapper.eq("type", 0);

        List<DistributionBalanceDetailDO> distributionBalanceDetailDOList = distributionBalanceDetailMapper.selectList(wrapper);
        if (!CollectionUtils.isEmpty(distributionBalanceDetailDOList)) {
            for (DistributionBalanceDetailDO detailDO : distributionBalanceDetailDOList) {
                if (detailDO.getFreezeBalanceDelta() >= 0) {
                    unfreezeBalance(detailDO.getFreezeBalanceDelta(), detailDO.getDistributionId(), orderNo, detailDO.getDescription().replace("冻结佣金", "解冻佣金"));
                } else {
                    throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_HAS_BEEN_UNFREEZE);
                }
            }
        }
    }


    /***  针对具体订单
     * 临时用一次解救一下问题订单
     * @param orderNo
     * @throws ServiceException
     */
    public void unfreezeOrder_help(String orderNo) throws ServiceException {
        Wrapper<DistributionBalanceDetailDO> wrapper = new EntityWrapper<DistributionBalanceDetailDO>();
        if (orderNo != null) {
            wrapper.eq("order_no", orderNo);
        }
        //wrapper.eq("type", 0);

        List<DistributionBalanceDetailDO> distributionBalanceDetailDOList = distributionBalanceDetailMapper.selectList(wrapper);
        if (!CollectionUtils.isEmpty(distributionBalanceDetailDOList)) {
            for (DistributionBalanceDetailDO detailDO : distributionBalanceDetailDOList) {
                if (detailDO.getFreezeBalanceDelta() >= 0) {
                    unfreezeBalance(detailDO.getFreezeBalanceDelta(), detailDO.getDistributionId(), orderNo, detailDO.getDescription().replace("冻结佣金", "解冻佣金"));
                } else {
                    throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_HAS_BEEN_UNFREEZE);
                }
            }
        }
    }







    /**
     * 订单退款
     *
     * @param orderNo
     * @throws ServiceException
     */
    public void refundOrder(String orderNo) throws ServiceException {
        List<DistributionBalanceDetailDO> distributionBalanceDetailDOList = distributionBalanceDetailMapper.selectList(new EntityWrapper<DistributionBalanceDetailDO>().eq("order_no", orderNo));
        if (!CollectionUtils.isEmpty(distributionBalanceDetailDOList)) {
            for (DistributionBalanceDetailDO detailDO : distributionBalanceDetailDOList) {
                if (detailDO.getFreezeBalanceDelta() >= 0) {
                    incFreezeBalance(-detailDO.getFreezeBalanceDelta(), detailDO.getDistributionId(), orderNo, detailDO.getDescription().replace("冻结佣金", "退还佣金"));
                } else {
                    throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_HAS_BEEN_UNFREEZE);
                }
            }
        }
    }

    /**
     * 获取用户余额
     *
     * @param userId
     * @return
     * @throws ServiceException
     */
    public Integer balance(Long userId) throws ServiceException {
        List<DistributionDO> distributionDOList = distributionMapper.selectList(new EntityWrapper<DistributionDO>().eq("user_id", userId));
        if (CollectionUtils.isEmpty(distributionDOList)) {
            throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_NOT_EXIST);
        }
        return distributionDOList.get(0).getBalance();
    }

    /**
     * 调用出一定要加事务
     * 为订单创建抵扣单，并返回抵扣单的Id
     *
     * @param deductionSum
     * @param userId
     * @return
     */
    public Long createDeduction(OrderDO order,Integer deductionSum, Long userId) throws ServiceException {
        // 1.创建抵扣单
        Date now = new Date();
        DistributionDeductionDO distributionDeductionDO = new DistributionDeductionDO();
        distributionDeductionDO.setStatus(DistributionDeductionStatusType.WAIT_CONFIRM.getCode());
        distributionDeductionDO.setSum(deductionSum);
        distributionDeductionDO.setUserId(userId);
        distributionDeductionDO.setGmtUpdate(now);
        distributionDeductionDO.setGmtCreate(now);
        if (distributionDeductionMapper.insert(distributionDeductionDO) > 0) {
            // 2.冻结余额
            // 2.1.锁行，并校验表是否一致
            DistributionDO distributionDO = distributionMapper.selectByUserIdForUpdate(userId);
            //DistributionBalanceDetailDO lastDetail = distributionBalanceDetailMapper.getDistributionUserLastForUpdate(distributionDO.getId());
            //判断两个数据是否一致
            //if (!lastDetail.getFreezeBalance().equals(distributionDO.getFreezeBalance()) || !lastDetail.getBalance().equals(distributionDO.getBalance())) {
            //    throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_YOUR_DATA_NOT_CONSTANCY);
            //}
            // 2.2.更新主表
            DistributionDO distribution = distributionMapper.selectById(distributionDO.getId());
            Integer realDeductionSum = getRealDeductionSum(distribution, deductionSum);
            if (distributionMapper.decBalance(distributionDO.getId(), deductionSum,realDeductionSum) > 0) {
                // 2.3.创建明细单，将抵扣的
                DistributionBalanceDetailDO balanceDetailDO = new DistributionBalanceDetailDO();
                balanceDetailDO.setFreezeBalance(distribution.getFreezeBalance());
                balanceDetailDO.setFreezeBalanceDelta(0);
                balanceDetailDO.setBalance(distribution.getBalance() - deductionSum);
                balanceDetailDO.setRealBalance(distribution.getRealBalance() - realDeductionSum);
                balanceDetailDO.setTotalBalance(distribution.getTotalBalance());
                balanceDetailDO.setTotalRealBalance(distribution.getTotalRealBalance());
                balanceDetailDO.setBalanceDelta(-deductionSum);
                balanceDetailDO.setRealBalanceDelta(-realDeductionSum);
                balanceDetailDO.setOrderNo(order.getOrderNo());
                balanceDetailDO.setDescription("会员卡购物抵扣-RMB" + deductionSum / 100.0);
                balanceDetailDO.setGmtUpdate(now);
                balanceDetailDO.setGmtCreate(now);
                balanceDetailDO.setType(DistributionBalanceDetailType.EXPENSE_DEDUCTION.getCode());
                balanceDetailDO.setDistributionId(distribution.getId());
                if (distributionBalanceDetailMapper.insert(balanceDetailDO) > 0) {
                    return distributionDeductionDO.getId();
                }
            }
        }
        throw new AppServiceException(ExceptionDefinition.APP_UNKNOWN_EXCEPTION);
    }


    /**
     * 计算当前用户的  要扣减的金额
     * realBalance 扣减 -> totalRealBalance / totalBalance  * deductionSum
     * balance 扣减 -> deductionSum
     * @param distributionDO
     * @return
     */
    public Integer getRealDeductionSum(DistributionDO distributionDO,Integer deductionSum){
        Integer realDeductionSum = 0;
        Double ratio = 0D;
        Integer totalBalance = distributionDO.getBalance();
        if(totalBalance == null){
            totalBalance =0;
        }
        Integer totalRealBalance = distributionDO.getRealBalance();
        if(totalRealBalance == null || totalRealBalance == 0){
            totalRealBalance = 1;
        }
        ratio=MoneyUtils.div(totalRealBalance.doubleValue(),totalBalance.doubleValue(),4);
        Double deductionSumDou = MoneyUtils.mul(ratio, deductionSum.doubleValue());
        realDeductionSum = MoneyUtils.div(deductionSumDou,1D,0).intValue();
        return realDeductionSum;
    }

    /**
     * 退还用户抵扣佣金 (全额)
     *
     * @param deductionId
     * @return
     * @throws ServiceException
     */
    public void returnDeduction(OrderDO orderDO,Long deductionId, Integer sum, Long userId) throws ServiceException {
        DistributionDeductionDO distributionDeductionDO = distributionDeductionMapper.selectById(deductionId);
        if (distributionDeductionDO != null && distributionDeductionDO.getStatus() == DistributionDeductionStatusType.WAIT_CONFIRM.getCode()) {
            // 1.将抵扣单状态退回
            Date now = new Date();
            DistributionDeductionDO updateDeductionDO = new DistributionDeductionDO();
            updateDeductionDO.setId(deductionId);
            updateDeductionDO.setStatus(DistributionDeductionStatusType.RETURNED.getCode());
            updateDeductionDO.setGmtUpdate(now);
            if (distributionDeductionMapper.updateById(updateDeductionDO) > 0) {
                // 2.归还佣金
                // 2.1.锁行，并校验表是否一致
                DistributionDO distributionDO = distributionMapper.selectByUserIdForUpdate(userId);
               // DistributionBalanceDetailDO lastDetail = distributionBalanceDetailMapper.getDistributionUserLastForUpdate(distributionDO.getId());
                //判断两个数据是否一致
                //if (!lastDetail.getBalance().equals(distributionDO.getBalance())) {
                //    throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_YOUR_DATA_NOT_CONSTANCY);
                //}
                // 2.2.更新主表
                Integer deductionSum = distributionDeductionDO.getSum();
                if (sum != null) {
                    // 若sum存在
                    deductionSum = sum;
                }
                //退还佣金也记录一个明细 将用户的可用余额增加回来并记录详情  抛弃解冻
                //if (distributionMapper.unfreezeBalance(distributionDO.getId(), deductionSum) > 0) {
                //
                DistributionDO distribution = distributionMapper.selectById(distributionDO.getId());
                Integer returnReal = getReturnReal(orderDO, distribution, deductionSum);
                if (distributionMapper.incBalance(distributionDO.getId(), deductionSum,returnReal) > 0) {
                    // 2.3 更新明细表
                    DistributionBalanceDetailDO balanceDetailDO = new DistributionBalanceDetailDO();
                    balanceDetailDO.setFreezeBalance(distributionDO.getFreezeBalance());
                    balanceDetailDO.setFreezeBalanceDelta(0);
                    balanceDetailDO.setBalance(distribution.getBalance() + deductionSum);
                    balanceDetailDO.setTotalBalance(distribution.getTotalBalance());
                    balanceDetailDO.setTotalRealBalance(distribution.getTotalRealBalance());
                    balanceDetailDO.setRealBalance(distribution.getRealBalance());
                    balanceDetailDO.setBalanceDelta(+deductionSum);
                    balanceDetailDO.setRealBalanceDelta(+returnReal);
                    balanceDetailDO.setOrderNo(orderDO.getOrderNo());
                    balanceDetailDO.setDescription("会员卡购物抵扣(退款/取消)退还-RMB" + deductionSum / 100.0);
                    balanceDetailDO.setGmtUpdate(now);
                    balanceDetailDO.setGmtCreate(now);
                    balanceDetailDO.setType(DistributionBalanceDetailType.VIPCARDRECHARGE.getCode());
                    balanceDetailDO.setDistributionId(distribution.getId());
                    if (distributionBalanceDetailMapper.insert(balanceDetailDO) > 0) {
                        return;
                    }
                }
            }
            throw new AppServiceException(ExceptionDefinition.APP_UNKNOWN_EXCEPTION);
        }
        throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_DEDUCTION_STATUS_INCORRECT);
    }

    /**
     * 获取退款真实金额
     * @param order
     * @param distributionDO
     * @param deductionSum
     * @return
     */
    public Integer getReturnReal(OrderDO order,DistributionDO distributionDO,Integer deductionSum) throws ServiceException  {
        if(order == null || StringUtils.isEmpty(order.getOrderNo())){
            return deductionSum;
        }
        //获取当前订单时的余额消费流水
        List<DistributionBalanceDetailDO> distributionBalanceDetailDOS = distributionBalanceDetailMapper
                .selectList(new EntityWrapper<DistributionBalanceDetailDO>()
                        .eq("distribution_id", distributionDO.getId())
                        .eq("order_no", order.getOrderNo())
                        .eq("type", DistributionBalanceDetailType.EXPENSE_DEDUCTION.getCode())
                        .lt("balance_delta",0));
        //如果流水中查询不到 订单消费流水  不计算实付金额
        if(distributionBalanceDetailDOS == null || distributionBalanceDetailDOS.size() < 1){
            return deductionSum;
        }
        DistributionBalanceDetailDO distributionBalanceDetailDO = distributionBalanceDetailDOS.get(0);
        Integer totalBalance = distributionBalanceDetailDO.getBalance() == 0? 1:distributionBalanceDetailDO.getBalance();
        Integer totalRealBalance = distributionBalanceDetailDO.getRealBalance() == 0? 1:distributionBalanceDetailDO.getRealBalance();
        Double discount =MoneyUtils.div(totalRealBalance.doubleValue(),totalBalance.doubleValue(),4);
        Double realBalanceDeltaDou = MoneyUtils.mul(discount, deductionSum.doubleValue());
        Integer realBalanceDelta = MoneyUtils.div(realBalanceDeltaDou,1D,0).intValue();
        //如果没有 实付金额数据  返回退款金额
        if(realBalanceDelta == null || realBalanceDelta == 0){
            return deductionSum;
        }
        if(realBalanceDelta < 0){
            realBalanceDelta = realBalanceDelta * -1;
        }
        return realBalanceDelta;
    }

    /**
     * 退还用户抵扣佣金 (部分)
     *
     * 暂时给门店专用
     *
     * @param deductionId
     * @return
     * @throws ServiceException
     */
    public void returnStoreDeduction(OrderDO orderDO,Long deductionId, Integer sum, Long userId) throws ServiceException {
        DistributionDeductionDO distributionDeductionDO = distributionDeductionMapper.selectById(deductionId);
        if (distributionDeductionDO != null && distributionDeductionDO.getStatus() == DistributionDeductionStatusType.WAIT_CONFIRM.getCode()) {
            // 1.将抵扣单状态退回
            Date now = new Date();
            DistributionDeductionDO updateDeductionDO = new DistributionDeductionDO();
            //updateDeductionDO.setId(deductionId);
            //updateDeductionDO.setStatus(DistributionDeductionStatusType.RETURNED.getCode());
            //updateDeductionDO.setGmtUpdate(now);
           // if (distributionDeductionMapper.updateById(updateDeductionDO) > 0) {
                // 2.归还佣金
                // 2.1.锁行，并校验表是否一致
                DistributionDO distributionDO = distributionMapper.selectByUserIdForUpdate(userId);
                // DistributionBalanceDetailDO lastDetail = distributionBalanceDetailMapper.getDistributionUserLastForUpdate(distributionDO.getId());
                //判断两个数据是否一致
                //if (!lastDetail.getBalance().equals(distributionDO.getBalance())) {
                //    throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_YOUR_DATA_NOT_CONSTANCY);
                //}
                // 2.2.更新主表
                Integer deductionSum = distributionDeductionDO.getSum();
                if (sum != null ) {
                    // 若sum存在
                    deductionSum = sum;
                }
                //退还佣金也记录一个明细 将用户的可用余额增加回来并记录详情  抛弃解冻
                //if (distributionMapper.unfreezeBalance(distributionDO.getId(), deductionSum) > 0) {
            DistributionDO distribution = distributionMapper.selectById(distributionDO.getId());
            Integer returnReal = getReturnReal(orderDO, distributionDO, deductionSum);
                if (distributionMapper.incBalance(distributionDO.getId(), deductionSum,returnReal) > 0) {

                    // 2.3 更新明细表
                    DistributionBalanceDetailDO balanceDetailDO = new DistributionBalanceDetailDO();
                    balanceDetailDO.setFreezeBalance(distributionDO.getFreezeBalance());
                    balanceDetailDO.setFreezeBalanceDelta(0);
                    balanceDetailDO.setBalance(distribution.getBalance() + deductionSum);
                    balanceDetailDO.setRealBalance(distribution.getRealBalance() + returnReal);
                    balanceDetailDO.setTotalBalance(distribution.getTotalBalance());
                    balanceDetailDO.setTotalRealBalance(distribution.getTotalRealBalance());
                    balanceDetailDO.setBalanceDelta(+deductionSum);
                    balanceDetailDO.setRealBalanceDelta(+returnReal);
                    balanceDetailDO.setOrderNo(orderDO.getOrderNo());
                    balanceDetailDO.setDescription("会员卡购物抵扣(退款/取消)退还-RMB" + deductionSum / 100.0);
                    balanceDetailDO.setGmtUpdate(now);
                    balanceDetailDO.setGmtCreate(now);
                    balanceDetailDO.setType(DistributionBalanceDetailType.VIPCARDRECHARGE.getCode());
                    balanceDetailDO.setDistributionId(distribution.getId());
                    if (distributionBalanceDetailMapper.insert(balanceDetailDO) > 0) {
                        return;
                    }
                }
           // }
            throw new AppServiceException(ExceptionDefinition.APP_UNKNOWN_EXCEPTION);
        }
        throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_DEDUCTION_STATUS_INCORRECT);
    }



    /**
     * 确认分销用户抵扣佣金
     *
     * @param deductionId
     * @throws ServiceException
     */
    public void confirmDeduction(Long deductionId, Long userId) throws ServiceException {
        DistributionDeductionDO distributionDeductionDO = distributionDeductionMapper.selectById(deductionId);
        if (distributionDeductionDO != null && distributionDeductionDO.getStatus() == DistributionDeductionStatusType.WAIT_CONFIRM.getCode()) {
            // 1.将抵扣单确认
            Date now = new Date();
            DistributionDeductionDO updateDeductionDO = new DistributionDeductionDO();
            updateDeductionDO.setId(deductionId);
            updateDeductionDO.setStatus(DistributionDeductionStatusType.CONFIRMED.getCode());
            updateDeductionDO.setGmtUpdate(now);
            if (distributionDeductionMapper.updateById(updateDeductionDO) > 0) {
                return;
                // 2.归还佣金
                // 2.1.锁行，并校验表是否一致
                /*DistributionDO distributionDO = distributionMapper.selectByUserIdForUpdate(userId);
                DistributionBalanceDetailDO lastDetail = distributionBalanceDetailMapper.getDistributionUserLastForUpdate(distributionDO.getId());
                //判断两个数据是否一致
                if (!lastDetail.getBalance().equals(distributionDO.getBalance())) {
                    throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_YOUR_DATA_NOT_CONSTANCY);
                }
                //TODO 2.2.更新主表【将抵扣的钱从解冻的额度中减掉 同时增加一条余额记录】
                // TODO 主表减少可用佣金费用 只做确认详细记录
                //if (distributionMapper.decFreezeBalance(distributionDO.getId(), distributionDeductionDO.getSum()) > 0) {
                if (distributionMapper.decBalance(distributionDO.getId(), distributionDeductionDO.getSum()) > 0) {
                    // 2.3 更新明细表
                    DistributionBalanceDetailDO balanceDetailDO = new DistributionBalanceDetailDO();
                    Integer deductionSum = distributionDeductionDO.getSum();
                    balanceDetailDO.setFreezeBalance(distributionDO.getFreezeBalance());
                    balanceDetailDO.setFreezeBalanceDelta(0);
                    balanceDetailDO.setBalance(distributionDO.getBalance()-distributionDeductionDO.getSum());
                    balanceDetailDO.setBalanceDelta(distributionDeductionDO.getSum());
                    balanceDetailDO.setOrderNo("");
                    balanceDetailDO.setDescription("会员卡购物抵扣-确认收货-RMB" + deductionSum / 100.0);
                    balanceDetailDO.setGmtUpdate(now);
                    balanceDetailDO.setGmtCreate(now);
                    balanceDetailDO.setType(1);
                    balanceDetailDO.setDistributionId(distributionDO.getId());
                    if (distributionBalanceDetailMapper.insert(balanceDetailDO) > 0) {
                        return;
                    }
                }*/
            }
        }
        throw new AppServiceException(ExceptionDefinition.DISTRIBUTION_DEDUCTION_STATUS_INCORRECT);
    }

    /**
     * 获取分销用户排行榜
     *
     * @param pageNo
     * @param pageSize
     * @return
     * @throws ServiceException
     */
    public Page<DistributionRankingDTO> getRankingPage(Integer pageNo, Integer pageSize) throws ServiceException {
        List<DistributionRankingDTO> list = distributionMapper.getDistributionRanking((pageNo - 1) * pageSize, pageSize);
        Integer count = distributionMapper.selectCount(null);
        return new Page<>(list, pageNo, pageSize, count);
    }

    /**
     * 获取分销核销订单
     *
     * @param pageNo
     * @param pageSize
     * @param distributionId
     * @return
     * @throws ServiceException
     */
    public Page<DistributionOrderDTO> getDistributionOrderPage(Integer pageNo, Integer pageSize, Long distributionId) throws ServiceException {
        List<DistributionOrderDTO> list = distributionBalanceDetailMapper.getDistributionOrder((pageNo - 1) * pageSize, pageSize, distributionId);
        Integer count = distributionBalanceDetailMapper.selectCount(
                new EntityWrapper<DistributionBalanceDetailDO>()
                        .eq("distribution_id", distributionId)
                        .gt("balance_delta", 0)
                        .ne("order_no", ""));
        return new Page<>(list, pageNo, pageSize, count);
    }

    /**
     * 获取推广的用户
     *
     * @param distributionId
     * @return
     * @throws ServiceException
     */
    public List<DistributionUserDTO> getDistributionUserPage(Long distributionId) throws ServiceException {
        // 通过select distinct userId 所有核销订单的用户Id。通过 select user in 来查找出所有用户即可。但是这样无法获取到该用户核销的总金额。
        // 由于订单里面没保存核销金额。所以若需要获取核销总金额，只能将该用户订单全部查出来（作为子查询），然后用in去明细表里面查。
        List<DistributionUserDTO> distributionUserIds = distributionMapper.getDistributionUser(distributionId);
        if (!CollectionUtils.isEmpty(distributionUserIds)) {
            // 封装用户信息
            distributionUserIds.forEach(item -> {
                UserDO userDO = userMapper.getUserNicknameAndAvartar(item.getUserId());
                item.setNickname(userDO.getNickname());
                item.setAvatarUrl(userDO.getAvatarUrl());
            });
        }
        return distributionUserIds;
    }

    public List<DistributionDetailExcel> exportExcelQuery(String gmtStart, String gmtEnd, Integer changeType){
        List<DistributionDetailExcel> result= new ArrayList<>();
        Date start = new Date();
        Date end = new Date();
        if (!StringUtils.isEmpty(gmtStart) && !StringUtils.isEmpty(gmtEnd)) {
            try {
                start = DateUtils.getDateByStr(gmtStart, "yyyy-MM-dd\nhh:mm:ss");
                end = DateUtils.getDateByStr(gmtEnd,"yyyy-MM-dd\nhh:mm:ss");
            } catch (ParseException e) {
            }
        }
        result= distributionBalanceDetailMapper.getDistributionBalanceDetailExcel(start,end,changeType);
        for ( DistributionDetailExcel distributionDetailExcel: result
             ) {
            Double balanceDou = MoneyUtils.div(distributionDetailExcel.getBalance().doubleValue(), 100d, 2);
            Double balanceDeltaDou = MoneyUtils.div(distributionDetailExcel.getBalanceDelta().doubleValue(), 100d, 2);
            distributionDetailExcel.setBalanceDeltaExcel(balanceDeltaDou + "");
            distributionDetailExcel.setBalanceExcel(balanceDou + "");
        }
        return result;
    }

    /**
     *
     * @param gmtStart
     * @param gmtEnd
     */
        public List<DistributionStatisicExcelExport> exportExcelStatistic(String gmtStart, String gmtEnd){
            Date start = new Date();
            Date end = new Date();
            if (!StringUtils.isEmpty(gmtStart) && !StringUtils.isEmpty(gmtEnd)) {
                try {
                    start = DateUtils.getDateByStr(gmtStart, "yyyy-MM-dd\nhh:mm:ss");
                    end = DateUtils.getDateByStr(gmtEnd,"yyyy-MM-dd\nhh:mm:ss");
                } catch (ParseException e) {
                }
            }
            Integer up= 1;
            Integer down= 2;
            //总账额
            List<DistributionStatisicDTO> allAccountList = distributionBalanceDetailMapper.allAccount(start, end);
            Map<String, DistributionStatisicDTO> allAccountMap = allAccountList.stream().collect(Collectors.toMap(DistributionStatisicDTO::getDateKey, a -> a, (k1, k2) -> k1));
            //总充值
            List<DistributionStatisicDTO> allUpUserCountList = distributionBalanceDetailMapper.allUserCount(start,end,up);
            Map<String, DistributionStatisicDTO> allUpUserCountMap = allUpUserCountList.stream().collect(Collectors.toMap(DistributionStatisicDTO::getDateKey, a -> a, (k1, k2) -> k1));
            //总消耗
            List<DistributionStatisicDTO> allDownUserCountList = distributionBalanceDetailMapper.allUserCount(start,end,down);
            Map<String, DistributionStatisicDTO> allDownUserCountMap = allDownUserCountList.stream().collect(Collectors.toMap(DistributionStatisicDTO::getDateKey, a -> a, (k1, k2) -> k1));
            //当天充值
            List<DistributionStatisicDTO> rechList = distributionBalanceDetailMapper.rechAndCons(start,end,up);
            Map<String, DistributionStatisicDTO> rechMap = rechList.stream().collect(Collectors.toMap(DistributionStatisicDTO::getDateKey, a -> a, (k1, k2) -> k1));
            //当天消费
            List<DistributionStatisicDTO> consList = distributionBalanceDetailMapper.rechAndCons(start,end,down);
            Map<String, DistributionStatisicDTO> consMap = consList.stream().collect(Collectors.toMap(DistributionStatisicDTO::getDateKey, a -> a, (k1, k2) -> k1));
            List<String> everyDays = DateUtils.getEveryDays(start, end);
            List<DistributionStatisicExcelExport> result = new ArrayList<>();
            for (String dateKey : everyDays
                 ) {
                DistributionStatisicExcelExport excel=new DistributionStatisicExcelExport();
                excel.setDateKey(dateKey);
                //总账额
                DistributionStatisicDTO allAccountdto = allAccountMap.get(dateKey);
                Double allAccount = allAccountdto != null ? allAccountdto.getMoney() : 0d;
                excel.setAllAccount(allAccount+"");
                //总充值人数
                DistributionStatisicDTO allUpUserCountdto = allUpUserCountMap.get(dateKey);
                Integer allUpUserCount = allUpUserCountdto != null ? allUpUserCountdto.getCount() : 0;
                excel.setAllUpUserCount(allUpUserCount+"");
                //总消耗人数
                DistributionStatisicDTO allDownUserCoundto = allDownUserCountMap.get(dateKey);
                Integer allDownUserCound = allDownUserCoundto != null ? allDownUserCoundto.getCount() : 0;
                excel.setAllDownUserCount(allDownUserCound+"");
                //当天充值总额
                DistributionStatisicDTO rechdto = rechMap.get(dateKey);
                Double rech = rechdto != null ? rechdto.getMoney() : 0d;
                excel.setRech(rech+"");
                //当天消费总额
                DistributionStatisicDTO consdto = consMap.get(dateKey);
                Double cons = consdto != null ? consdto.getMoney() : 0d;
                excel.setCons(cons+"");
                result.add(excel);
            }
            return result;
        }

    /**
     * 统计用户充值与消费
     * @param gmtStart
     * @param gmtEnd
     */
    public List<UserCardDetailedExcel> exportExcelUserRechargeConsumptionStatistic(String gmtStart, String gmtEnd){
        List<UserCardDetailedExcel> userCardDetailedExcels=new ArrayList<>();
        Date start = new Date();
        Date end = new Date();
        if (!StringUtils.isEmpty(gmtStart) && !StringUtils.isEmpty(gmtEnd)) {
            try {
                start = DateUtils.getDateByStr(gmtStart, "yyyy-MM-dd\nhh:mm:ss");
                end = DateUtils.getDateByStr(gmtEnd,"yyyy-MM-dd\nhh:mm:ss");
            } catch (ParseException e) {
            }
        }
        //获取所有用户
        List<UserDO> userIdAndNickname = userMapper.getUserIdAndNickname();
        //用户充值信息
        List<UserAccountDTO> userRechargeDTOS = distributionBalanceDetailMapper.userRecharge(start, end);
        Map<Long, UserAccountDTO> userRechargetMap = userRechargeDTOS.stream().collect(Collectors.toMap(UserAccountDTO::getUserId, Function.identity(), (key1, key2) -> key1));
        //用户消费信息
        Map<Long,List<UserAccountDTO>> storeUserConsumeMap = new HashMap<>();
        Map<Long,Map<Long, UserAccountDTO>> serAccountDTOMap = new HashMap<>();
        //用户期初
        List<UserAccountDTO> userStartAccountDTOS = distributionBalanceDetailMapper.userEndAccount(start);
        Map<Long, UserAccountDTO> userStartAccountMap = userStartAccountDTOS.stream().collect(Collectors.toMap(UserAccountDTO::getUserId, a -> a, (k1, k2) -> k1));
        //用户期末余额
        List<UserAccountDTO> userEndAccountDTOS = distributionBalanceDetailMapper.userEndAccount(end);
        Map<Long, UserAccountDTO> userEndAccountMap = userEndAccountDTOS.stream().collect(Collectors.toMap(UserAccountDTO::getUserId, a -> a, (k1, k2) -> k1));
        for ( UserDO userDO: userIdAndNickname
             ) {
            UserCardDetailedExcel userExcel=new UserCardDetailedExcel();
            userExcel.setUserId(userExcel.getUserId());
            userExcel.setUserName("用户未填写用户名");
            if(org.apache.commons.lang3.StringUtils.isNotEmpty(userDO.getNickname())){
                userExcel.setUserName(userDO.getNickname());
            }
            //用户期初
            UserAccountDTO userStart = userStartAccountMap.get(userDO.getId());
            userExcel.setStartBalance(0f);
            if(userStart != null && userStart.getPrice() !=null){
                userExcel.setStartBalance(userStart.getPrice());
            }
            //用户充值
            UserAccountDTO userRechar = userRechargetMap.get(userDO.getId());
            userExcel.setRechargePrice(0f);
            if(userRechar != null && userRechar.getPrice() != null){
                userExcel.setRechargePrice(userRechar.getPrice());
            }
            //用户消费   （区分门店）
            List<UserStoreConsume> userStoreConsumes =new ArrayList<>();
            userExcel.setUserStoreConsumes(userStoreConsumes);
            //用户期末
            UserAccountDTO userEndAccountDTO = userEndAccountMap.get(userDO.getId());
            userExcel.setEndBalance(0f);
            if(userEndAccountDTO != null){
                userExcel.setEndBalance(userEndAccountDTO.getPrice());
            }
            userCardDetailedExcels.add(userExcel);
        }

        //会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡
        //会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡
        //会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡
        //会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡会员卡
        //会员卡退费 不关注制卡时间是否再
        Map<String, UserAccountDTO> cardReturnMap=new HashMap<>();
        List<UserAccountDTO> cardReturn = userCardMapper.cardReturnStatistic(start, end);
        if(cardReturn != null){
            cardReturnMap = cardReturn.stream().collect(Collectors.toMap(UserAccountDTO::getCardNo, a -> a, (k1, k2) -> k1));
        }
        //制卡时间在  统计区间内  期初  0 充值 为 卡的额度 + 退款额
        List<UserAccountDTO> withinCard  = userCardMapper.getCardByStatistic(start, end);
        Map<String, UserAccountDTO> withinCardMap = withinCard.stream().collect(Collectors.toMap(UserAccountDTO::getCardNo, a -> a, (k1, k2) -> k1));
        //充值   （制卡时的卡额度）
        List<UserAccountDTO> withinRecharge = userCardMapper.cardRechargeStatistic(null, end);
        Map<String, UserAccountDTO> withinRechargeMap = withinRecharge.stream().collect(Collectors.toMap(UserAccountDTO::getCardNo, a -> a, (k1, k2) -> k1));
        //门店消费
        Map<Long, List<UserAccountDTO>> withinConsumeStoreMap=new HashMap<>();
        List<UserAccountDTO> withinConsume = userCardMapper.cardConsumeStatistic(start, end);
        Map<Long,Map<String, UserAccountDTO>> cardAccountDTOMap = new HashMap<>();

        //期末
        List<UserAccountDTO> withinEnd = userCardMapper.cardBalanceStatistic(end);
        Map<String, UserAccountDTO> withinEndMap = withinEnd.stream().collect(Collectors.toMap(UserAccountDTO::getCardNo, a -> a, (k1, k2) -> k1));
        for (UserAccountDTO cardAccountDTO : withinCard
             ) {
            UserCardDetailedExcel userExcel=new UserCardDetailedExcel();
            userExcel.setCardNo(cardAccountDTO.getCardNo());
            //期初   为0
            userExcel.setStartBalance(0f);
            //充值
            UserAccountDTO withinRechar = withinRechargeMap.get(cardAccountDTO.getCardNo());
            userExcel.setRechargePrice(0f);
            if(withinRechar != null && withinRechar.getPrice() != null){
                userExcel.setRechargePrice(withinRechar.getPrice());
            }
            //退费添加到充值中
            UserAccountDTO cardReturndto = cardReturnMap.get(cardAccountDTO.getCardNo());
            if(cardReturndto != null && cardReturndto.getPrice() != null){
                Double rechargePrice = MoneyUtils.add(userExcel.getRechargePrice().doubleValue(), cardReturndto.getPrice().doubleValue());
                userExcel.setRechargePrice(rechargePrice.floatValue());
            }
            List<UserStoreConsume> userStoreConsumes =new ArrayList<>();
            //消费
            userExcel.setUserStoreConsumes(userStoreConsumes);
            UserAccountDTO userAccountDTO = withinEndMap.get(cardAccountDTO.getCardNo());
            userExcel.setEndBalance(0f);
            if(userAccountDTO != null && userAccountDTO.getPrice() != null){
                userExcel.setEndBalance(userAccountDTO.getPrice());
            }
            userCardDetailedExcels.add(userExcel);
        }
        //制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前
        //制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前
        //制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前
        //制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前
        //制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前制卡时间在  统计开始时间之前
        //区间前的卡
        List<UserAccountDTO> beforeCard = userCardMapper.getCardByStatistic(null, start);
        Map<String, UserAccountDTO> beforeCardMap = beforeCard.stream().collect(Collectors.toMap(UserAccountDTO::getCardNo, a -> a, (k1, k2) -> k1));
        //期初
        List<UserAccountDTO> beforeStart = userCardMapper.cardBalanceStatistic(start);
        Map<String, UserAccountDTO> beforeStartMap = beforeStart.stream().collect(Collectors.toMap(UserAccountDTO::getCardNo, a -> a, (k1, k2) -> k1));
        //充值   0
        //消费   消费可以不区分制卡时间
        //期末
        List<UserAccountDTO> beforeEnd = userCardMapper.cardBalanceStatistic(end);
        Map<String, UserAccountDTO> beforeEndMap = beforeEnd.stream().collect(Collectors.toMap(UserAccountDTO::getCardNo, a -> a, (k1, k2) -> k1));
        for (UserAccountDTO cardAccountDTO : beforeCard
             ) {
            UserCardDetailedExcel userExcel=new UserCardDetailedExcel();
            userExcel.setCardNo(cardAccountDTO.getCardNo());
            //期初
            userExcel.setStartBalance(0f);
            UserAccountDTO beforeStar = beforeStartMap.get(cardAccountDTO.getCardNo());
            if(beforeStar != null && beforeStar.getPrice() != null){
                userExcel.setStartBalance(beforeStar.getPrice());
            }
            //充值
            userExcel.setRechargePrice(0f);
            //退费添加到充值中
            UserAccountDTO cardReturndto = cardReturnMap.get(cardAccountDTO.getCardNo());
            if(cardReturndto != null && cardReturndto.getPrice() != null){
                Double rechargePrice = MoneyUtils.add(userExcel.getRechargePrice().doubleValue(), cardReturndto.getPrice().doubleValue());
                userExcel.setRechargePrice(rechargePrice.floatValue());
            }
            //制卡为查询区间前  激活为查询区间内
            if(userExcel.getStartBalance().floatValue() == 0){
                UserAccountDTO userAccountDTO = withinRechargeMap.get(cardAccountDTO.getCardNo());
                if(userAccountDTO != null){
                    Float rechargePrice = userExcel.getRechargePrice();
                    Double rechargePriceAdd = MoneyUtils.add(rechargePrice.doubleValue(), userAccountDTO.getPrice().doubleValue());
                    userExcel.setRechargePrice(rechargePriceAdd.floatValue());
                }
            }
            //消费
            List<UserStoreConsume> userStoreConsumes =new ArrayList<>();
            userExcel.setUserStoreConsumes(userStoreConsumes);
            //期末
            userExcel.setEndBalance(0f);
            UserAccountDTO userAccountDTO = beforeEndMap.get(cardAccountDTO.getCardNo());
            if(userAccountDTO != null && userAccountDTO.getPrice() != null){
                userExcel.setEndBalance(userAccountDTO.getPrice());
            }
            userCardDetailedExcels.add(userExcel);
        }
        UserCardDetailedExcel total=new UserCardDetailedExcel();
        List<UserStoreConsume> totalConsumeList=new ArrayList<>();
        Double totalRecharge = 0d;
        Double totalConsumes = 0d;
        totalRecharge = userCardDetailedExcels.stream().mapToDouble(UserCardDetailedExcel::getRechargePrice).sum();
        total.setRechargePrice(totalRecharge.floatValue());
        for (UserCardDetailedExcel excel :userCardDetailedExcels
             ) {
            Double sum=0d;
            List<UserStoreConsume> userStoreConsumes = excel.getUserStoreConsumes();
            for (UserStoreConsume userStoreConsume : userStoreConsumes
                 ) {
                if(userStoreConsume == null || userStoreConsume.getStoreConsumPrice() == null){
                        continue;
                }
                sum = MoneyUtils.add(sum,userStoreConsume.getStoreConsumPrice().doubleValue());
            }
            totalConsumes = MoneyUtils.add(sum,totalConsumes);
        }
        List<UserStoreConsume> userStoreConsumes=new ArrayList<>();
        UserStoreConsume userStoreConsume=new UserStoreConsume();
        userStoreConsume.setStoreConsumPrice(totalConsumes.floatValue());
        userStoreConsume.setStoreName("门店消费合计");
        userStoreConsumes.add(userStoreConsume);
        total.setUserStoreConsumes(userStoreConsumes);
        userCardDetailedExcels.add(total);
        return  userCardDetailedExcels;
    }
}
